//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

#include <winhack.h>
#include <elasys_server.h>
#include <driver.h>
#include <stdio.h>
#include <sysconfig.h>
#include <serial.h>
#include "serialio.h"
#include "_win32/win32_serial.h"


ECode Serial::Read(
    /* [in] */ Int64 u64Offset,
    /* [in] */ Int32 bytesToRead,
    /* [out] */ MemoryBuf * pBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    if (pBuffer == NULL || !pBuffer->GetCapacity() ||
        (int)bytesToRead > pBuffer->GetCapacity() ||
        0 == bytesToRead) {
        return E_INVALID_ARGUMENT;
    }

    win32_serial_read(
        (serial_res_t *) m_devio,
        (unsigned char *)pBuffer->GetPayload(),
        bytesToRead,
        (unsigned int*)&bytesToRead);
    pBuffer->SetUsed(bytesToRead);

    return NOERROR;
}

ECode Serial::Write(
    /* [in] */ Int64 u64Offset,
    /* [in] */ const MemoryBuf & buffer,
    /* [out] */ Int32 * pBytesWritten,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    if (buffer.IsNullOrEmpty()
        || NULL == pBytesWritten) {
        return E_INVALID_ARGUMENT;
    }

    win32_serial_write(
        (serial_res_t *) m_devio,
        (unsigned char *)buffer.GetPayload(),
        buffer.GetUsed(),
        (unsigned int*)pBytesWritten);

    return NOERROR;
}

ECode Serial::Control(
    /* [in] */ Handle32 nControlCode,
    /* [in] */ const MemoryBuf & inBuffer,
    /* [out] */ MemoryBuf * pOutBuffer,
    /* [out] */ IEvent * * ppCompletionEvent)
{
    INT eno;
    UINT uNumberOfBytes;
    UINT subfunc;

    switch (nControlCode & SERIALCONTROL_OPERATIONMASK) {
    case SERIALCONTROL_GETCONFIG:
        if (pOutBuffer == NULL) { uNumberOfBytes = 0; }
        else { uNumberOfBytes = (UINT) pOutBuffer->GetCapacity(); }

        subfunc = nControlCode & SERIALCONTROL_OBJECTMASK;
        if (subfunc == IO_GET_CONFIG_SERIAL_BUFFER_INFO) {
            if (uNumberOfBytes < sizeof(serial_buf_info_t)) {
                return E_INVALID_ARGUMENT;
            }
            serial_buf_info_t buf_info;
            buf_info.rx_bufsize = 2000;
            buf_info.rx_count = win32_serial_GetInQueueSize(
                (serial_res_t *) m_devio);
            buf_info.tx_bufsize = 2000;
            buf_info.tx_count = 0;
            pOutBuffer->Copy((Byte*)&buf_info, sizeof(buf_info));
        }
        else if (subfunc == IO_GET_CONFIG_SERIAL_OUTPUT_FLUSH) {
            win32_serial_FlushOutput((serial_res_t *) m_devio);
        }
        else if (subfunc == IO_GET_CONFIG_SERIAL_INPUT_FLUSH) {
            win32_serial_FlushInput((serial_res_t *) m_devio);
        }
        else if (subfunc == IO_GET_CONFIG_SERIAL_MODEM_STATUS) {
        }
        else if (subfunc == IO_GET_CONFIG_SERIAL_ABORT) {
            win32_serial_abort((serial_res_t *) m_devio);
        }
        return NOERROR;

    case SERIALCONTROL_SETCONFIG:
        if (inBuffer.IsNull()) { uNumberOfBytes = 0; }
        else { uNumberOfBytes = (UINT) inBuffer.GetUsed(); }

        eno = NOERROR;
        subfunc = nControlCode & SERIALCONTROL_OBJECTMASK;
        if (subfunc == IO_SET_CONFIG_READ_BLOCKING) {
            if (uNumberOfBytes < sizeof(UINT)) { return E_INVALID_ARGUMENT; }
            UINT *block = (UINT *) (char *) inBuffer.GetPayload();
            if (*block) {
                win32_serial_SetCommBlock((serial_res_t *) m_devio, TRUE);
            }
            else {
                win32_serial_SetCommBlock((serial_res_t *) m_devio, FALSE);
            }
        }
        else if (subfunc == IO_SET_CONFIG_SERIAL_TIMEOUT) {
            if (uNumberOfBytes < sizeof(SERIAL_TIMEOUTS)) { return E_INVALID_ARGUMENT; }
            SERIAL_TIMEOUTS *timeouts =
                (SERIAL_TIMEOUTS *) (char *) inBuffer.GetPayload();
            win32_serial_SetCommTimeouts(
                (serial_res_t *) m_devio,
                timeouts);
        }
        else if (subfunc == IO_SET_CONFIG_SERIAL_INFO) {
            if (uNumberOfBytes < sizeof(SERIAL_INFO)) { return E_INVALID_ARGUMENT; }
            SERIAL_INFO *info = (SERIAL_INFO *) (char *) inBuffer.GetPayload();
            win32_serial_SetSerialInfo(
                (serial_res_t *) m_devio,
                info);
        }
        else {
            return E_INVALID_ARGUMENT;
        }
        return NOERROR;

    case SERIALCONTROL_WAITEVENT: {
        if (pOutBuffer == NULL) { uNumberOfBytes = 0; }
        else { uNumberOfBytes = (UINT) pOutBuffer->GetCapacity(); }
        if (win32_serial_GetInQueueSize((serial_res_t *) m_devio) == 0) {
            win32_serial_WaitForEvent((serial_res_t *) m_devio);
        }
        if (uNumberOfBytes < sizeof(INT)) {
            return E_INVALID_ARGUMENT;
        }
        *(INT *) (char *) pOutBuffer->GetPayload() = NUM_EV_RXCHAR;
        pOutBuffer->SetUsed(sizeof(INT));
        return NOERROR;
    }

    default:
        return E_INVALID_ARGUMENT;
    }
}

void Serial::Dispose()
{
    win32_serial_uninit((serial_res_t *)m_devio);
    delete this;
}

ECode Serial::Initialize(INT nPortNum)
{
    m_devio = win32_serial_init(nPortNum);
    if (m_devio == NULL) {
        return E_ABORT;
    }
    return NOERROR;
}

IDeviceDriver * CDECL CreateSerial(INT portId)
{
    Serial *serialObj = new Serial;
    if (serialObj == NULL) { return NULL; }
    if (SUCCEEDED(serialObj->Initialize(portId))) {
        serialObj->AddRef();
        return (IDeviceDriver *)serialObj;
    }
    else {
        delete serialObj;
        return NULL;
    }

    return NULL;
}

